package com.ant.storage.service.impl;

import com.ant.storage.common.ResultCode;
import com.ant.storage.common.ResultVo;
import com.ant.storage.dto.DepDto;
import com.ant.storage.dto.DingDto;
import com.ant.storage.dto.PickingDto;
import com.ant.storage.handler.AntException;
import com.ant.storage.mapper.InventoryMapper;
import com.ant.storage.mapper.PickingMapper;
import com.ant.storage.pojo.*;
import com.ant.storage.service.*;
import com.ant.storage.utils.DingDingUtil;
import com.ant.storage.utils.RandomUtil;
import com.ant.storage.vo.PickingCensusVo;
import com.ant.storage.vo.PickingCommodityVo;
import com.ant.storage.wechat.WeChatUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.taobao.api.ApiException;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author melon
 * @since 2022-10-28
 */
@Service
public class PickingServiceImpl extends ServiceImpl<PickingMapper, Picking> implements IPickingService {

    @Autowired
    IPickingDetailsService pickingDetailsService;

    @Autowired
    IPickingService pickingService;

    @Autowired
    IInventoryService inventoryService;

    @Resource
    InventoryMapper inventoryMapper;

    @Autowired
    IInventoryLogService inventoryLogService;

    @Resource
    private IAuditListService auditListService;

    @Autowired
    public ServletContext servletContext;

    @Autowired
    IDepartmentService departmentService;

    @Autowired
    public IApprovalMsgService approvalMsgService;

    @Override
    public PageInfo<Picking> querylist(PickingDto dto) {
        UserInfo userInfo = (UserInfo) servletContext.getAttribute("userInfo");
        if (userInfo == null) {
            throw new AntException(ResultCode.FAILURE.getCode(), "登录超时");
        }
        PageHelper.startPage(dto.getPageIndex(), dto.getPageSize());
        QueryWrapper<Picking> wrapper = new QueryWrapper<>();

        if (dto.getWarehouseId() != null) {
            wrapper.in("warehouse_id", dto.getWarehouseId());
        }
        if (StringUtils.isNotEmpty(dto.getOddNumbers())) {
            wrapper.like("odd_numbers", dto.getOddNumbers());
        }
        if (StringUtils.isNotEmpty(dto.getBusinessOrderNumber())) {
            wrapper.like("business_order_number", dto.getBusinessOrderNumber());
        }
        if (StringUtils.isNotEmpty(dto.getStatus())) {
            wrapper.eq("status", dto.getStatus());
        }
        if (StringUtils.isNotEmpty(dto.getDepId())) {
            wrapper.eq("dep_id", dto.getDepId());
        }
        if (dto.getCollectionType() != null) {
            wrapper.eq("collection_type", dto.getCollectionType());
        }
        if (dto.getAuditStatus() != null) {
            wrapper.eq("audit_status", dto.getAuditStatus());
        }
        if (dto.getTimes() != null && dto.getTimes().size() != 0) {
            wrapper.ge("delivery_time", dto.getTimes().get(0) + " 00:00:00");
            wrapper.le("delivery_time", dto.getTimes().get(1) + " 23:59:59");
        }

        if (userInfo.getRoles() == null) {
            wrapper.lambda().in(Picking::getUserId, userInfo.getNameId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("admin")) {
            wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("admins")) {
            wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("warehouse")) {
            wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("price")) {
            wrapper.lambda().in(Picking::getUserId, userInfo.getNameId());
        }  else if (dto.getUserId() != null && userInfo.getRoles() !=null && !userInfo.getRoles().equals("admins")) {
            wrapper.lambda().in(Picking::getUserId, dto.getUserId());
        }
        wrapper.eq("is_delete", 1);
        wrapper.orderByDesc("id");
        List<Picking> pickingList = this.baseMapper.selectList(wrapper);
        pickingList.stream().map(item -> {
            //查詢商品
            List<PickingCommodityVo> PickingDetails = this.baseMapper.findByIdList(item.getId());
            item.setDetailsList(PickingDetails);
            List<AuditList> audit = auditListService.list(new QueryWrapper<AuditList>().eq("parent_id",
                    item.getId()).eq("process_type", "picking"));
            item.setAuditList(audit);
            return item;
        }).collect(Collectors.toList());
        PageInfo<Picking> pageInfo = new PageInfo<>(pickingList);
        return pageInfo;
    }

    @Override
    public PageInfo<Picking> querylist2(PickingDto dto) {
        UserInfo userInfo = (UserInfo) servletContext.getAttribute("userInfo");
        if (userInfo == null) {
            throw new AntException(ResultCode.FAILURE.getCode(), "登录超时");
        }
        PageHelper.startPage(dto.getPageIndex(), dto.getPageSize());
        QueryWrapper<Picking> wrapper = new QueryWrapper<>();

        if (dto.getWarehouseId() != null) {
            wrapper.in("warehouse_id", dto.getWarehouseId());
        }
        if (StringUtils.isNotEmpty(dto.getOddNumbers())) {
            wrapper.like("odd_numbers", dto.getOddNumbers());
        }
        if (StringUtils.isNotEmpty(dto.getBusinessOrderNumber())) {
            wrapper.like("business_order_number", dto.getBusinessOrderNumber());
        }
        if (StringUtils.isNotEmpty(dto.getStatus())) {
            wrapper.eq("status", dto.getStatus());
        }
        if (StringUtils.isNotEmpty(dto.getDepId())) {
            wrapper.eq("dep_id", dto.getDepId());
        }
        if (dto.getCollectionType() != null) {
            wrapper.eq("collection_type", dto.getCollectionType());
        }
        if (dto.getAuditStatus() != null) {
            wrapper.eq("audit_status", dto.getAuditStatus());
        }
        if (dto.getTimes() != null && dto.getTimes().size() != 0) {
            wrapper.ge("delivery_time", dto.getTimes().get(0) + " 00:00:00");
            wrapper.le("delivery_time", dto.getTimes().get(1) + " 23:59:59");
        }

        if (userInfo.getRoles() == null) {
            wrapper.lambda().in(Picking::getUserId, userInfo.getNameId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("admin")) {
            wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("admins")) {
            wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("warehouse")) {
            wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
        }
        if (userInfo.getRoles() != null && userInfo.getRoles().equals("price")) {
            wrapper.lambda().in(Picking::getUserId, userInfo.getNameId());
        }  else if (dto.getUserId() != null && userInfo.getRoles() !=null && !userInfo.getRoles().equals("vadmin")) {
            wrapper.lambda().in(Picking::getUserId, dto.getUserId());
        }
        wrapper.eq("is_delete", 1);
        wrapper.orderByDesc("id");
        List<Picking> pickingList = this.baseMapper.selectList(wrapper);
        PageInfo<Picking> pageInfo = new PageInfo<>(pickingList);
        return pageInfo;
    }

    private void fillInfo(List<Picking> list){
        Set<Integer> idset=list.stream().map(Picking::getId).collect(Collectors.toSet());
        List<Picking> pickings=pickingService.list(new QueryWrapper<Picking>().in("id", idset));
        List<PickingDetails> details=pickingDetailsService.list(new QueryWrapper<PickingDetails>().in("picking_id", idset));

        Map<Integer,List<Picking>> map1=pickings.stream().collect(Collectors.groupingBy(Picking::getId));
        Map<Integer,PickingDetails> map2=details.stream().collect(Collectors
                .toMap(PickingDetails::getPickingId, item -> item,(k1,k2)->k1));



//        list.forEach(item-> {
//            item.set(det.get(item.getId()));
//            item.setPicking(pic.get(item.getId()));
//            return item;
//        });

    }



    @Override
    public PageInfo<Picking> getPickingDepList(PickingDto dto) {
        try {
            PageHelper.startPage(dto.getPageIndex(), dto.getPageSize());
            QueryWrapper<Picking> wrapper = new QueryWrapper<>();
            if (StringUtils.isNotEmpty(dto.getDepId())) {
                wrapper.eq("dep_id", dto.getDepId());
            }
            if (dto.getTimes() != null && dto.getTimes().size() != 0) {
                wrapper.between("creation_time", dto.getTimes().get(0), dto.getTimes().get(1));
            }
            UserInfo userInfo = (UserInfo) servletContext.getAttribute("userInfo");
            if (userInfo.getRoles().equals("admin")) {
                wrapper.lambda().in(Picking::getCityId, userInfo.getCityId());
                if (dto.getUserId() != null) {
                    wrapper.eq("user_id", userInfo.getNameId());
                }
            }

            wrapper.eq("is_delete", 1);
            wrapper.orderByDesc("id");
            List<Picking> pickingList = this.baseMapper.selectList(wrapper);
            pickingList.stream().map(item -> {
                //查詢商品
                List<PickingCommodityVo> PickingDetails = this.baseMapper.findDepByIdList(item.getId(),
                        dto.getGoodsType());
                item.setDetailsList(PickingDetails);
                List<AuditList> audit = auditListService.list(new QueryWrapper<AuditList>().eq("parent_id",
                        item.getId()).eq("process_type", "picking"));
                item.setAuditList(audit);
                item.setDepartment(departmentService.getDepName(item.getDepId()));
                return item;
            }).collect(Collectors.toList());
            if (dto.getGoodsType() != null && StringUtils.isNotEmpty(dto.getGoodsType())) {
                pickingList =
                        pickingList.stream().filter(t -> t.getDetailsList().size() > 0).collect(Collectors.toList());
            }
            PageInfo<Picking> pageInfo = new PageInfo<>(pickingList);
            return pageInfo;
        } catch (Exception e) {
            throw new AntException(ResultCode.BODY_NOT_MATCH);
        }
    }


    @Override
    public Map<String, Object> getPickingByDepIdList(PickingDto dto) {
        try {
            Map<String, Object> map = new HashMap<>();
            List<Picking> pickingList = this.baseMapper.selectDepByIdList(dto);
            pickingList.stream().map(item -> {
                //查詢商品
                List<PickingCommodityVo> PickingDetails = this.baseMapper.findDepByIdList(item.getId(),
                        dto.getGoodsType());
                item.setDetailsList(PickingDetails);
                return item;
            }).collect(Collectors.toList());

            Integer count = this.baseMapper.selectDepByIdListCount(dto);
            map.put("data", pickingList);
            map.put("total", count);
            return map;
        } catch (Exception e) {
            throw new AntException(ResultCode.FAILURE.getResultCode(), e.getMessage());
        }
    }


    @Override
    public Picking getPickingByID(Integer id) {
        Picking pickingList = this.baseMapper.selectOne(new QueryWrapper<Picking>().eq("id", id));
        if (pickingList == null) {
            throw new AntException(ResultCode.NOT_FOUND);
        }
        //查詢商品
        List<PickingCommodityVo> PickingDetails = this.baseMapper.findByIdList(pickingList.getId());
        List<AuditList> list = auditListService.list(new QueryWrapper<AuditList>().eq("parent_id", pickingList.getId())
                .eq("process_type", "picking"));
        pickingList.setAuditList(list);
        pickingList.setDepartment(departmentService.getDepName(pickingList.getDepId()));
        pickingList.setDetailsList(PickingDetails);
        return pickingList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVo savePicking(Picking picking) throws NullPointerException, IOException {
        synchronized (this) {
          //  UserInfo userInfo = (UserInfo) servletContext.getAttribute("userInfo");
            picking.setOddNumbers("NL" + RandomUtil.getOrderNumbers(this.baseMapper.selectLastID()));
            picking.setCreationTime(new Date());
            picking.setIsDelete(1);
            if (picking.getId() == null) {
                picking.setAuditStatus(picking.getIsApproved() == 0 ? 2 : 1);
                this.baseMapper.insert(picking);
                //WebSocketServer.sendInfo(picking.getId().toString(), null);
            }
            if (picking.getDetailsList().size() > 0) {
                picking.getDetailsList().stream().map(item -> {
                    PickingDetails details = new PickingDetails();
                    details.setId(item.getId());
                    details.setInventoryId(item.getInventoryId());
                    details.setPickingId(picking.getId());
                    details.setActualQuantity(item.getActualQuantity() == "0" || item.getActualQuantity() == "" ?
                            null : item.getApplyNumber());
                    details.setApplyNumber(item.getApplyNumber() == "0" || item.getApplyNumber() == "" ? null :
                            item.getApplyNumber());
                    details.setPrice(item.getGoodsPrice());//单价
                    details.setIsDelete(1);
                    pickingDetailsService.save(details);
                    return details;
                }).collect(Collectors.toList());
                //钉钉审核提交
                if (picking.getIsApproved() != 0) {
                    AuditList auditList = new AuditList();
                    auditList.setParentId(picking.getId());
                    auditList.setName(picking.getUserId().toString());
                    auditList.setProcessType("picking");
                    auditList.setCreationTime(new Date());
                    // 添加审核记录
                    for (String str : picking.getApproval()) {
                        auditList.setNameId(str);
                        auditList.setTypes("1");
                        auditListService.save(auditList);
                    }
                    for (String str2 : picking.getNotify()) {
                        auditList.setNameId(str2);
                        auditList.setTypes("2");
                        auditListService.save(auditList);
                    }
                    // 发起钉钉审批
                    String str = addWeChatData(picking);
                    UpdateWrapper<Picking> updateWrapper = new UpdateWrapper<Picking>().set(
                            "process_instance_id", str).eq("id", picking.getId());
                    this.update(updateWrapper);
                    ApprovalMsg msg = new ApprovalMsg();
                    msg.setParentId(picking.getId());
                    msg.setApprovalType("picking");
                    msg.setApprovalUser(picking.getUserName());
                    msg.setApprovalUserId(picking.getUserId());
                    msg.setSpNo(str);
                    msg.setCreationTime(new Date());
                    approvalMsgService.save(msg);
                    BigDecimal reduce = picking.getDetailsList().stream().map(p ->
                            p.getCommodityPrice().multiply(new BigDecimal(p.getActualQuantity() == null ? "0" :
                                    p.getActualQuantity())))
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                    pickingService.update(new UpdateWrapper<Picking>().set("price", reduce).eq("id", picking.getId()));
                    return ResultVo.success("保存成功");
                }
                return ResultVo.success("保存成功");
            } else {
                throw new AntException(ResultCode.PARAM_IS_BLANK);
            }
        }

    }

    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public String addWeChatData(Picking dto) throws AntException {
        InvoiceInfo info = new InvoiceInfo();
        UserInfo userInfo = (UserInfo) servletContext.getAttribute("userInfo");
        info.setWeChat(userInfo.getWeChat());
        info.setUserName(userInfo.getUserName());
        info.setInvoiceName("物资领料申请");
        info.setReimbursementTypeText("物资领料申请");
        info.setApproval(dto.getApproval());
        info.setNotify(dto.getNotify());
        info.setInvoicePrice(new BigDecimal("0"));
        info.setStartTime(null);
        info.setEndTime(null);
        info.setInvoiceNotes(dto.getRemarks());
        info.setAccountName(null);
        info.setAccountNumber(null);
        info.setAccountBank(null);
        List<ReimburseDetail> list = new ArrayList<>();
        if (dto.getDetailsList() != null && dto.getDetailsList().size() > 0) {
            dto.getDetailsList().forEach(item -> {
                ReimburseDetail detail = new ReimburseDetail();
                detail.setProjectName(item.getCommodityName());
                detail.setProjectAmount(item.getActualQuantity());//数量
                detail.setProjectUnit(item.getCommodityUnits());//单位
                detail.setProjectPrice(item.getGoodsPrice());//单价
                list.add(detail);
            });
        }
        info.setDetailList(list);
//        System.out.println("list=>>"+list);
//        return "202408030001";
        String str = WeChatUtils.launchWeChatData(info);
        return str;
    }

    /**
     * 提交钉钉审批
     *
     * @param dto
     * @return
     */
    @SneakyThrows
    @Transactional
    public String addDingDing(DingDto dto) throws ApiException {
        dto.setAccess_token(DingDingUtil.obtainToken());
        String process_instance_id = DingDingUtil.OldGoodePickingApprove(dto);
        return process_instance_id;
    }

    @Override
    @Transactional()
    public ResultVo pickingComplete(Picking picking) {
        synchronized (this) {
            Picking pickingServiceOne = pickingService.getOne(new QueryWrapper<Picking>()
                    .eq("id", picking.getId()).eq("is_delete", 1));
            if (pickingServiceOne != null && picking.getDetailsList().size() > 0) {
                picking.getDetailsList().stream().map(item -> {
                    PickingDetails details = new PickingDetails();
                    details.setId(item.getId());
                    details.setInventoryId(item.getInventoryId());
                    details.setPickingId(picking.getId());
                    details.setActualQuantity(item.getActualQuantity() == "0" || item.getActualQuantity() == "" ? "" :
                            item.getActualQuantity());
                    details.setApplyNumber(item.getApplyNumber() == "0" || item.getApplyNumber() == "" ? "" :
                            item.getApplyNumber());
                    details.setPrice(item.getCommodityPrice());
                    pickingDetailsService.updateById(details);
                    return details;
                }).collect(Collectors.toList());

                BigDecimal reduce = picking.getDetailsList().stream().map(p ->
                        p.getCommodityPrice().multiply(new BigDecimal(p.getActualQuantity() == null || p.getActualQuantity() == "" ? "0" :
                                p.getActualQuantity())))
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                pickingService.update(new UpdateWrapper<Picking>().set("price", reduce)
                        .set("status", picking.getStatus())
                        .set("creation_by", picking.getCreationBy())
                        .set("delivery_time", new Date())
                        .eq("id", picking.getId()));

                if (picking.getStatus().equals("2")) {

                    List<PickingDetails> pickingDetails = pickingDetailsService.list(new QueryWrapper<PickingDetails>()
                            .eq("picking_id", picking.getId()).eq("is_delete", 1));
                    pickingDetails.stream().map(item -> {
                        boolean b = verifyInventory(item.getInventoryId(), new BigDecimal(item.getActualQuantity()));
                        if (b) {
                            Inventory serviceOne = inventoryService.getOne(new QueryWrapper<Inventory>().eq("id",
                                    item.getInventoryId()));
                            BigDecimal amout =
                                    serviceOne.getGoodsAmount().subtract(new BigDecimal(item.getActualQuantity()));
                            amout.setScale(2, BigDecimal.ROUND_HALF_UP);

                            inventoryService.update(new UpdateWrapper<Inventory>()
                                    .set("goods_amount", amout)
                                    .eq("id", item.getInventoryId()));
                            Inventory one = inventoryService.getOne(new QueryWrapper<Inventory>().eq("id",
                                    item.getInventoryId()));
                            InventoryLog log = new InventoryLog();
                            log.setWarehouseId(one.getWarehouseId());
                            log.setInventoryId(item.getInventoryId());
                            log.setGoodsAmount(item.getActualQuantity() == "0" || item.getActualQuantity() == "" ?
                                    null : item.getActualQuantity());
                            log.setGoodsPrice(item.getPrice());
                            log.setStockState(2);
                            log.setSubscribeId(picking.getId());
                            log.setRemark("领料出库");
                            log.setCreatorBy(picking.getCreationBy());
                            log.setCreationTime(new Date());
                            inventoryLogService.save(log);
                            return item;
                        } else {
                            String commodityName = inventoryMapper.selectCommodityName(item.getInventoryId());
                            throw new AntException(ResultCode.FAILURE.getResultCode(), commodityName + "，存不足无法出库!");
                        }
                    }).collect(Collectors.toList());
                    return ResultVo.success("操作成功");
                }
                return ResultVo.success("操作成功");
            }
            return ResultVo.fail("领料单不存在");
        }
    }

    //判断库存是否为0
    public boolean verifyInventory(Integer id, BigDecimal number) {
        Inventory one = inventoryService.getOne(new QueryWrapper<Inventory>().eq("id", id));
        if (one != null) {
            if (one.getGoodsAmount().subtract(number).compareTo(BigDecimal.ZERO) < 0) {
                return false;
            } else {
                return true;
            }
        } else {
            return false;
        }
    }


    @Override
    public List<PickingCensusVo> findPickingCensus(DepDto dto) {
        UserInfo userInfo = (UserInfo) servletContext.getAttribute("userInfo");
        if (userInfo != null) {
            dto.setCityId(userInfo.getCityId());
        }
        return this.baseMapper.findPickingCensus(dto);
    }

    @Override
    public AuditList selectIsAudit(String processInstanceId, String user_id) {
        Picking process_instance_id = this.getOne(new QueryWrapper<Picking>().eq("process_instance_id",
                processInstanceId));
        if (process_instance_id != null) {
            List<AuditList> parentId = auditListService.list(new QueryWrapper<AuditList>()
                    .eq("parent_id", process_instance_id.getId())
                    .eq("process_type", "picking")
                    .eq("types", "1")
                    .eq("name_id", user_id));
            if (parentId != null || parentId.size() > 0) {
                return parentId.get(0);
            }
            return null;
        }
        return null;
    }


}
